/*
*
*	The Gimme ECMAScript Library by Stephen Stchur
*	"Taking the pain out of Javascript!"
*
*	Copyright (c) Microsoft Corporation.  All rights reserved.
*
*	Description:
*	Gimme Animation Module:	Bezier Curve Interpolation, plus a number of default curves to aid in creating animations in Javascript.
*						This module is designed to work with the Gimme Effects extension.
*
*	Requires:
*	gimme.query.js
*
*/


Gimme.Animation = new function()
{
    var m_animationHash = {};
	var m_grouping = false;
	
	this.Speeds =
	{
		snail: 2000,
		turtle: 1250,
		slowly: 1250,
		rabbit: 1000,
		greyhound: 750,
		quickly: 750,
		cheetah: 500,
		lightning: 250
	};
	
	this.Directions = 
	{
		vertically: 1,
		horizontally: 2,
		both: 3
	};
    
	this.start = function(_guid, _fn, _milliseconds)
	{
		var iid = setInterval(_fn, _milliseconds);
		
		var obj = m_animationHash[_guid];
		if (typeof obj === 'undefined')
		{
			m_animationHash[_guid] = { iids: [ iid ], callback: null };
		}
		else
		{
			obj.iids.push(iid);
		}
	};
    
    this.end = function(/*_guid*/)
    {
		var i, len = arguments.length;
		for (i = 0; i < len; i++)
		{
			var guid = arguments[i];
	        var obj = m_animationHash[guid];
	        if (typeof obj !== 'undefined')
	        {
				g(obj.iids).forEach(function(_iid, _index)
				{
					clearTimeout(_iid);
				});

				if (typeof obj.callback === 'function')
				{
					obj.callback.call();
					obj.callback = null;
				}
				delete m_animationHash[guid];			
	        }
		}
    };
	
	this.isRunning = function(_guid)
	{
		return typeof m_animationHash[_guid] !== 'undefined';
	};
	
	this.whenDone = function(_guid, _callback)
	{
		var obj = m_animationHash[_guid];
		if (typeof obj === 'undefined')
		{
			m_animationHash[_guid] = { iids: [], callback: _callback };
		}
		else
		{
			obj.callback = _callback;
		}
	};
	
	this.startGroup = function()
	{
		m_grouping = true;
	};
	
	this.endGroup = function()
	{
		m_grouping = false;
	};
	
	this.isGrouping = function()
	{
		return m_grouping === true;
	};
};


// BEZIER CURVE CLASS
Gimme.Animation.BezierCurve = function(/* controlPoints, pointsPerCurve (both pulled from arguments) */)
{
	this.points = [];
	this.args = arguments;
};
Gimme.Animation.BezierCurve.prototype.initialize = function()
{
	var args = this.args;
	var argsLen = args.length;
	var pointsPerCurve = args[argsLen - 1] || 100;
	var numCurves = argsLen - 1;

	var i, j, n, factorials, controlPoints;
	for (i = 0; i < numCurves; i++)
	{
		controlPoints = args[i];
		factorials = getFactorials(controlPoints.length);
		n = controlPoints.length - 1;
		for (j = 0; j <= pointsPerCurve; j++)
		{
			this.points.push(computePoint(j / pointsPerCurve));
		}
	}
	
	this.args = args = null;
	return pointsPerCurve;
	
	
	// Private helper functions
	function computePoint(t)
	{
		var i, sumX = 0, sumY = 0;
		for (i = 0; i <= n; i++)
		{
			var n_choose_i = factorials[n] / (factorials[i] * factorials[n - i]);
			var one_minus_t_pow_n_minus_i = Math.pow(1 - t, n - i);
			var t_pow_i = Math.pow(t, i);
		
			sumX += n_choose_i * controlPoints[i].x * one_minus_t_pow_n_minus_i * t_pow_i;
			sumY += n_choose_i * controlPoints[i].y * one_minus_t_pow_n_minus_i * t_pow_i;
		}
		
		return { x: sumX, y: sumY };
	}
	
	function getFactorials(n)
	{
	    var i, curr = 1, factorials = [1];
	    for (i = 1; i <= n; i++)
	    {
			curr *= i;
			factorials.push(curr);
	    }
		
	    return factorials;
	}
};
Gimme.Animation.BezierCurve.prototype.getPoint = function(_t)
{
	var pointsLen = this.points.length;
	if (pointsLen === 0)
	{
		pointsLen = this.initialize();
	}
	
	var index = Math.floor(_t * pointsLen);
	if (index > pointsLen - 1)
	{
		index = pointsLen - 1;
	}
	
	return this.points[index];
};
// END BEZIER CURVE CLASS

// ACCELERATION LINE CLASS
Gimme.Animation.AccelerationLine = function(_pointCluster, _numPoints)
{
	var interval = _pointCluster[_pointCluster.length - 1];
	var pointCluster = g(_pointCluster).map(function(_val)
	{
		return { x: _val / interval, y: 0 };
	});
	
	this.bezier = new Gimme.Animation.BezierCurve(pointCluster, _numPoints);
	this.points = this.bezier.points;
};
Gimme.Animation.AccelerationLine.prototype.getValue = function(_t)
{
	return this.bezier.getPoint(_t).x;
};
// END ACCELERATION LINE CLASS

// STOCK ACCELERATIONLINES
Gimme.Animation.AccelerationLines =
{
	zoom: new Gimme.Animation.AccelerationLine(
		[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
		500,
		501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520], 75),
		
	slowStartAccelerate: new Gimme.Animation.AccelerationLine(
		[0, 1, 2, 3, 8, 50], 100),
		
	quickStartDecelerate: new Gimme.Animation.AccelerationLine(
		[0, 50, 55, 56, 57, 58], 100),
	
	linear: new Gimme.Animation.AccelerationLine(
		[0,10], 100)
};
Gimme.Animation.AccelerationLines.defaultLine = Gimme.Animation.AccelerationLines.zoom;

